﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Components;

namespace WorkflowDesigner
{
    public class Node : Canvas
    {
        #region const
        public const double DashSize = 1.0;
        public const double StrokeThickness = 2.0;
        public const double StrokeHighlightThickness = 3.0;
        protected const int NotConnected = -1;
        #endregion

        #region events
        public event OnNodeSelected NodeSelected;
        #endregion

        #region private fields
        private bool _Initiated = false;
        private long LastMouseClickAt = 0;
        #endregion

        #region protected fields
        protected Brush strokeBrush, focusBrush, backgroundBrush, highlightBrush;
        protected Color backgroundColor, highlightColor, strokeColor, rubberbandColor, focusColor;
        protected Line outputLine;
        protected Point outputLeft, outputRight;
        protected double canvLeft, canvTop, highlightThickness = StrokeHighlightThickness;
        protected int currOutputNumber = 0;
        protected double beginX, beginY;
        protected double beginLineX, beginLineY;
        
        protected bool outputState = false,
                       trackingLineMovement = false,
                       trackingLineIsFromErrorPort = false;
        protected Point clickedPoint;
        #endregion

        #region props
        public Image Image { get; set; }
        public Size ImageSize { get; set; }
        public TextBox Label { get; set; }
        public string Description { get; set; }
        public Port InputPort { get; set; }
        public Port SuccessOutputPort { get; set; }
        public Port FailOutputPort { get; set; }
        public Rectangle SelectionRect { get; set; }
        public WorkflowComponentBase UserObject { get; set; }
        public List<Connector> FromConnectors { get; set; }
        public List<Connector> ToConnectors { get; set; }

        public bool Initiated
        {
            get 
            {
                return _Initiated; 
            }
            set
            {
                _Initiated = value;
                //this.Label.Visibility = this._Initiated ? Visibility.Visible : Visibility.Collapsed;
            }
        }

        private bool _Selected;
        public bool Selected
        {
            get
            {
                return _Selected;
            }
            set
            {
                if(this._Selected !=value)
                {
                    this._Selected = value;
                    if (this._Selected)
                        this.SelectionRect.Visibility = Visibility.Visible;
                    else
                        this.SelectionRect.Visibility = Visibility.Collapsed;
                }
            }
        }
        public bool Highlighted { get; set; }
        public bool TrackingMouseMovement { get; set; }
        #endregion

        #region ctor
        public Node():this(true)
        {}

        public Node(bool acceptInput)
            : this(acceptInput,
            "New activity", "Activity description",
            "StopWatch.png")
        {}

        public Node(WorkflowComponentBase component)
            :this(component.ActivityType!=WorkflowActivityType.Trigger,
            component.ComponentName, component.Description,
            component.ComponentImageName)
        {
            this.UserObject = component;
        }

        public Node(bool acceptInput,
             string nodeName, string description, 
            string imageFileName)
        {
            // setup the default colors
            backgroundColor = Color.FromArgb(0xaa, 0xff, 0xff, 0xff);
            highlightColor = Color.FromArgb(0xaa, 0xaa, 0xaa, 0xaa);
            strokeColor = Color.FromArgb(0xff, 0x54, 0x54, 0x54);
            rubberbandColor = Color.FromArgb(0xaa, 0, 0, 0);
            focusColor = Color.FromArgb(0xaa, 0x22, 0x8b, 0x22);
            // create brushes
            strokeBrush = new SolidColorBrush(strokeColor);
            // initialize this if "MouseEnter" is used
            focusBrush = null;
            backgroundBrush = new SolidColorBrush(backgroundColor);
            highlightBrush = new SolidColorBrush(highlightColor);

            this.Loaded += new RoutedEventHandler(Node_Loaded);
            this.FromConnectors = new List<Connector>();
            this.ToConnectors = new List<Connector>();
            this.ImageSize = new Size(48, 48);
            if (acceptInput)
            {
                this.InputPort = new Port(PortType.Input, this);
            }
            else
            {
                this.InputPort = null;
            }
            
            // port
            this.SuccessOutputPort = new Port(PortType.SuccessOutput, this);
            this.SuccessOutputPort.MouseLeftButtonDown += new MouseButtonEventHandler(Port_MouseLeftButtonDown);
            this.SuccessOutputPort.MouseLeftButtonUp += new MouseButtonEventHandler(Port_MouseLeftButtonUp);
            this.SuccessOutputPort.MouseMove += new MouseEventHandler(Port_MouseMove);
            this.FailOutputPort = new Port(PortType.ErrorOutput, this);
            this.FailOutputPort.MouseLeftButtonDown += new MouseButtonEventHandler(Port_MouseLeftButtonDown);
            this.FailOutputPort.MouseLeftButtonUp += new MouseButtonEventHandler(Port_MouseLeftButtonUp);
            this.FailOutputPort.MouseMove += new MouseEventHandler(Port_MouseMove);
            int zIndex = (int)this.GetValue(Canvas.ZIndexProperty);
            if (this.InputPort != null)
            {
                Children.Add(this.InputPort);
                this.InputPort.SetValue(Canvas.ZIndexProperty, zIndex + 1);
            }
            if (this.SuccessOutputPort != null)
            {
                Children.Add(this.SuccessOutputPort);
                this.SuccessOutputPort.SetValue(Canvas.ZIndexProperty, zIndex + 1);
            }
            if (this.FailOutputPort != null)
            {
                Children.Add(this.FailOutputPort);
                this.FailOutputPort.SetValue(Canvas.ZIndexProperty, zIndex + 1);
            }

            // selection rect
            this.SelectionRect = new Rectangle();
            this.SelectionRect.StrokeThickness = 2;
            this.SelectionRect.Fill = null;
            this.SelectionRect.Stroke = new SolidColorBrush(this.highlightColor);
            this.SelectionRect.Width = this.ImageSize.Width;
            this.SelectionRect.Height = this.ImageSize.Height;
            this.Children.Add(this.SelectionRect);

            this.Description = description;
            string imgUrl = string.Format("images/{0}", imageFileName);
            BitmapImage bmpImg = new BitmapImage(new Uri(imgUrl, UriKind.RelativeOrAbsolute));
            this.Image = new Image();
            this.Image.Source = bmpImg;
            this.Image.SetValue(WidthProperty, this.ImageSize.Width);
            this.Image.SetValue(HeightProperty, this.ImageSize.Height);
            if (this.Image != null)
            {
                Children.Add(this.Image);
            }
            this.Label = new TextBox();
            this.Label.Text = nodeName;
            this.Children.Add(this.Label);

            this.Selected = false;
        }
        #endregion

        #region init
        void Node_Loaded(object sender, RoutedEventArgs e)
        {
            this.canvLeft = (double) this.GetValue(Canvas.LeftProperty);
            this.canvTop = (double) this.GetValue(Canvas.TopProperty);
            Draw();
            if (this.Image != null)
            {
                this.Image.MouseLeftButtonDown += new MouseButtonEventHandler(OnMouseLeftButtonDown);
                this.Image.MouseLeftButtonUp += new MouseButtonEventHandler(OnMouseleftButtonUp);
                this.Image.MouseMove += new MouseEventHandler(OnMouseMove);
                this.Image.MouseEnter += new MouseEventHandler(OnMouseEnter);
                this.Image.MouseLeave += new MouseEventHandler(OnMouseLeave);
            }
            this.UpdateLabelPosition();
            this.UpdatePortPositions();
            this.UpdateSelectionRectanglePosition();
            this.Selected = false;
            this.SelectionRect.Visibility = Visibility.Collapsed;
        }

        private void UpdatePortPositions()
        {
            if (this.Image == null || double.IsNaN(this.Image.ActualWidth) || double.IsNaN(this.Image.ActualHeight))
                return;
            double x = (double) this.Image.GetValue(Canvas.LeftProperty);
            double y = (double) this.Image.GetValue(Canvas.TopProperty);
            if (this.InputPort != null)
            {
                this.InputPort.SetValue(Canvas.LeftProperty, x + this.Image.ActualWidth/2 - this.InputPort.PortSize.Width/2);
                this.InputPort.SetValue(Canvas.TopProperty, y);
            }
            if (this.SuccessOutputPort != null)
            {
                this.SuccessOutputPort.SetValue(Canvas.LeftProperty, x + this.Image.ActualWidth / 2 - this.SuccessOutputPort.PortSize.Width / 2);
                this.SuccessOutputPort.SetValue(Canvas.TopProperty, y + this.Image.ActualHeight);
            }
            if (this.FailOutputPort != null)
            {
                this.FailOutputPort.SetValue(Canvas.LeftProperty, x + this.Image.ActualWidth);
                this.FailOutputPort.SetValue(Canvas.TopProperty, y + this.Image.ActualHeight/2+this.FailOutputPort.PortSize.Height/2);
            }
        }

        private void UpdateLabelPosition()
        {
            double labelX = (double)this.Image.GetValue(Canvas.LeftProperty);
            double labelY = (double)this.Image.GetValue(Canvas.TopProperty) + this.Image.ActualHeight + 10;
            this.Label.SetValue(Canvas.LeftProperty, labelX);
            this.Label.SetValue(Canvas.TopProperty, labelY);
        }

        private void UpdateSelectionRectanglePosition()
        {
            double x = (double)this.Image.GetValue(Canvas.LeftProperty);
            double y = (double) this.Image.GetValue(Canvas.TopProperty);
            this.SelectionRect.SetValue(Canvas.LeftProperty, x);
            this.SelectionRect.SetValue(Canvas.TopProperty, y);
        }

        public void Draw()
        {
            outputLine = new Line();
            outputLine.Stroke = new SolidColorBrush(rubberbandColor);
            outputLine.StrokeThickness = StrokeThickness;
            outputLine.Visibility = System.Windows.Visibility.Collapsed;

            Children.Add(outputLine);
        }
        #endregion

        #region protected methods
        /// <summary>
        /// if successful, returns a positive integer representing the input number 
        /// where connection is made. If not successful, -1 is returned.
        /// </summary>
        /// <returns></returns>
        //protected abstract int Connect(Node node, Point clickedPort);

        protected virtual Point GetInputLocation(int inputNumber)
        {
            return new Point();
        }

        protected bool GetOutputState()
        {
            return outputState;
        }
        #endregion

        #region mouse handler for node 
        protected virtual void OnMouseEnter(object sender, RoutedEventArgs e)
        {
            if ((focusBrush == null) && this.Initiated)
                focusBrush = new SolidColorBrush(focusColor);
            if ((!TrackingMouseMovement) && this.Initiated)
            {
                //path.Stroke = focusBrush;
            }
            if (!this.Initiated)
            {
                //path.StrokeThickness = highlightThickness;
            }
            if (this.Initiated)
                this.Label.FontWeight = FontWeights.Bold;
        }

        protected virtual void OnMouseLeave(object sender, RoutedEventArgs e)
        {
            if (this.Initiated)
                this.Label.FontWeight = FontWeights.Normal;
        }

        protected virtual void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (DateTime.Now.Ticks - this.LastMouseClickAt > 2310000)
            {
                // single click 
                beginX = e.GetPosition(this).X;
                beginY = e.GetPosition(this).Y;
                TrackingMouseMovement = true;
                FrameworkElement fe = sender as FrameworkElement;
                canvLeft = Convert.ToDouble(fe.GetValue(Canvas.LeftProperty));
                canvTop = Convert.ToDouble(fe.GetValue(Canvas.TopProperty));
                if (this.Initiated)
                {
                    this.Selected = !this.Selected;
                    this.Highlight(this.Selected);
                }
                if (fe != null)
                    fe.CaptureMouse();
                
                e.Handled = true;
                if (this.NodeSelected != null)
                    this.NodeSelected(this);
            }
            else
            {
                // double click 
                this.Selected = true;
                this.Highlight(this.Selected);
                Type type = this.UserObject.GetType();
                if(WorkflowEngine.ComponentEditors !=null && WorkflowEngine.ComponentEditors.ContainsKey(type))
                {
                    Type editorType = WorkflowEngine.ComponentEditors[type];
                    ComponentEditorBase editor = (ComponentEditorBase) Activator.CreateInstance(editorType);
                    editor.Title = this.Label.Text + " Properties";
                    editor.LoadSettings(this, this.UserObject);
                    editor.ComponentUpdated += new OnComponentUpdated(editor_ComponentUpdated);
                    editor.Show();
                }
                
            }
            this.LastMouseClickAt = DateTime.Now.Ticks;
        }

        void editor_ComponentUpdated(object node, WorkflowComponentBase component)
        {
            ((Node) node).UserObject = component;
        }

        protected void OnMouseMove(object sender, MouseEventArgs e)
        {
            if (TrackingMouseMovement)
            {
                this.Selected = false;
                if (!this.Initiated)
                {
                    // ReplaceInstance((Panel)Parent);
                    this.Initiated = true;
                }
                double currentX = e.GetPosition(this).X;
                double currentY = e.GetPosition(this).Y;
                Image img = sender as Image;
                if(img ==null)
                    return;
                canvLeft = Convert.ToDouble(img.GetValue(Canvas.LeftProperty));
                canvTop = Convert.ToDouble(img.GetValue(Canvas.TopProperty));
                double newLeft = canvLeft + currentX - beginX;
                double newTop = canvTop + currentY - beginY;
                FrameworkElement fe2 = (FrameworkElement)this.Parent;
                double maxX, maxY;
                maxX = fe2.ActualWidth - this.Image.Width - 1;
                maxY = fe2.ActualHeight - this.Image.Height - 1;

                if (newLeft >= 0 && newLeft < maxX) beginX = currentX;
                if (newTop >= 0 && newTop < maxY) beginY = currentY;
                if (newLeft < 0)
                {
                    newLeft = 0;
                    beginX = clickedPoint.X;
                }
                if (newTop < 0)
                {
                    newTop = 0;
                    beginY = clickedPoint.Y;
                }
                newLeft = Math.Max(0, newLeft);
                newTop = Math.Max(0, newTop);
                img.SetValue(Canvas.LeftProperty, newLeft);
                img.SetValue(Canvas.TopProperty, newTop);
                canvLeft = newLeft;
                canvTop = newTop;

                // re-draw output
                DrawOutput();

                // have connected nodes redraw the output as well
                if (FromConnectors != null && this.FromConnectors.Count > 0)
                    foreach (Connector fromConn in this.FromConnectors)
                        if (fromConn != null && fromConn.FromPort != null)
                            fromConn.RedrawConnection();
                if (this.ToConnectors != null && this.ToConnectors.Count > 0)
                    foreach (Connector toConn in this.ToConnectors)
                        if (toConn != null && toConn.ToPort != null)
                            toConn.RedrawConnection();

                //if (this.InputPort != null)
                //    this.InputPort.UpdatePosition(new Point(canvLeft, canvTop), this.ImageSize);
                //if (this.SuccessOutputPort != null)
                //    this.SuccessOutputPort.UpdatePosition(new Point(canvLeft, canvTop), this.ImageSize);
                //if (this.FailOutputPort != null)
                //    this.FailOutputPort.UpdatePosition(new Point(canvLeft, canvTop), this.ImageSize);
                this.UpdatePortPositions();
                this.UpdateLabelPosition();
                this.UpdateSelectionRectanglePosition();
            }
        }

        protected virtual void OnMouseleftButtonUp(object sender, MouseButtonEventArgs e)
        {
            FrameworkElement selectedNode = sender as FrameworkElement;
            selectedNode.ReleaseMouseCapture();
            
            TrackingMouseMovement = false;


            if (!this.Initiated)
                OnMouseEnter(sender, e);
        }

        #endregion

        #region mouse handlers for port
        void Port_MouseMove(object sender, MouseEventArgs e)
        {
            if (trackingLineMovement)
            {
                if (!this.Initiated)
                {
                    return;
                }
                this.Selected = false;
                
                double currentX = e.GetPosition(this).X;
                double currentY = e.GetPosition(this).Y;
                bool rubberBandLineVisible = false;
                if (outputLine != null)
                    rubberBandLineVisible = (outputLine.Visibility == Visibility.Visible);
                if (rubberBandLineVisible)
                {
                    Panel parentPanel = (Panel)this.Parent;
                    double xScale = 1, yScale = 1;
                    if (Application.Current.Host.Content.IsFullScreen)
                    {
                        xScale = Application.Current.Host.Content.ActualWidth / parentPanel.ActualWidth;
                        yScale = Application.Current.Host.Content.ActualHeight / parentPanel.ActualHeight;
                    }
                    if (currentX < parentPanel.ActualWidth && currentX > 0 &&
                        currentY < parentPanel.ActualHeight && currentY > 0)
                    {
                        outputLine.X1 = beginLineX;
                        outputLine.Y1 = beginLineY;
                        outputLine.X2 = currentX;
                        outputLine.Y2 = currentY;
                        for (int i = 0; i < parentPanel.Children.Count; i++)
                        {
                            if (parentPanel.Children[i] is Node)
                            {
                                Node node = (Node)parentPanel.Children[i];
                                if (node.Contains(new Point(e.GetPosition(parentPanel).X * xScale,
                                                            e.GetPosition(parentPanel).Y * yScale)))
                                    node.Highlight(true);
                                else
                                    node.Highlight(false);
                            }
                        }
                    }
                }
            }
        }

        void Port_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            trackingLineMovement = false;
            Panel parentPanel = (Panel)Parent;
            Point mousePos = e.GetPosition(parentPanel);
            for (int i = 0; i < parentPanel.Children.Count; i++)
            {
                if (parentPanel.Children[i] is Node)
                {
                    if (outputLine != null)
                    {
                        if (outputLine.Visibility == System.Windows.Visibility.Visible)
                        {
                            Node node = (Node)parentPanel.Children[i];
                            if (node != this && node.Contains(mousePos))
                            {
                                node.Highlight(false);
                                if (trackingLineIsFromErrorPort)
                                    MakeConnection(PortType.ErrorOutput, node);
                                else
                                    MakeConnection(PortType.SuccessOutput, node);
                            }
                        }
                    }
                }
            }

            if (outputLine != null && outputLine.Visibility == System.Windows.Visibility.Visible)
            {
                outputLine.Visibility = System.Windows.Visibility.Collapsed;
            }
        }

        void Port_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            beginLineX = e.GetPosition(this).X;
            beginLineY = e.GetPosition(this).Y;
            trackingLineMovement = true;
            FrameworkElement fe = sender as FrameworkElement;
            double fromPosX = Convert.ToDouble(fe.GetValue(Canvas.LeftProperty));
            double fromPosY = Convert.ToDouble(fe.GetValue(Canvas.TopProperty));
            if (fe != null)
                fe.CaptureMouse();
            clickedPoint.X = beginLineX - fromPosX;
            clickedPoint.Y = beginLineY - fromPosY;
            if (outputLine != null && this.Initiated)
            {
                if (this.SuccessOutputPort.HitTest(new Point(beginLineX, beginLineY)))
                {
                    trackingLineIsFromErrorPort = false;
                    Point outputPoint = this.SuccessOutputPort.Position;
                    outputLine.X1 = outputPoint.X;
                    outputLine.Y1 = outputPoint.Y;
                    outputLine.X2 = beginLineX;
                    outputLine.Y2 = beginLineY;
                    outputLine.Visibility = System.Windows.Visibility.Visible;
                }
                else if (this.FailOutputPort.HitTest(new Point(beginLineX, beginLineY)))
                {
                    trackingLineIsFromErrorPort = true;
                    Point outputPoint = this.FailOutputPort.Position;
                    outputLine.X1 = outputPoint.X;
                    outputLine.Y1 = outputPoint.Y;
                    outputLine.X2 = beginLineX;
                    outputLine.Y2 = beginLineY;
                    outputLine.Visibility = System.Windows.Visibility.Visible;
                }
                else
                    outputLine.Visibility = System.Windows.Visibility.Collapsed;
            }
            e.Handled = true;
        }
        #endregion

        #region public methods
        public bool Contains(Point p)
        {
            Rect rect=new Rect(
                this.canvLeft, this.canvTop,
                this.Image.ActualWidth, this.Image.ActualHeight);
            return rect.Contains(p);
        }

        public void MoveTo(Point newPos)
        {
            double currentX = newPos.X;
            double currentY = newPos.Y;
            TrackingMouseMovement = true;
            canvLeft = Convert.ToDouble(this.Image.GetValue(Canvas.LeftProperty));
            canvTop = Convert.ToDouble(this.Image.GetValue(Canvas.TopProperty));
            double newLeft = canvLeft + currentX - beginX;
            double newTop = canvTop + currentY - beginY;
            FrameworkElement fe2 = (FrameworkElement)this.Parent;
            double maxX, maxY;
            maxX = fe2.ActualWidth - this.Image.Width - 1;
            maxY = fe2.ActualHeight - this.Image.Height - 1;

            if (newLeft >= 0 && newLeft < maxX) beginX = currentX;
            if (newTop >= 0 && newTop < maxY) beginY = currentY;
            if (newLeft < 0)
            {
                newLeft = 0;
                beginX = clickedPoint.X;
            }
            if (newTop < 0)
            {
                newTop = 0;
                beginY = clickedPoint.Y;
            }
            newLeft = Math.Max(0, newLeft);
            newTop = Math.Max(0, newTop);
            this.Image.SetValue(Canvas.LeftProperty, newLeft);
            this.Image.SetValue(Canvas.TopProperty, newTop);
            canvLeft = newLeft;
            canvTop = newTop;
            // re-draw output
            DrawOutput();

            // have connected nodes redraw the output as well
            if (FromConnectors != null && this.FromConnectors.Count > 0)
                foreach (Connector fromConn in this.FromConnectors)
                    if (fromConn != null && fromConn.FromPort != null)
                        fromConn.RedrawConnection();
            if (this.ToConnectors != null && this.ToConnectors.Count > 0)
                foreach (Connector toConn in this.ToConnectors)
                    if (toConn != null && toConn.ToPort != null)
                        toConn.RedrawConnection();

            this.UpdatePortPositions();
            this.UpdateLabelPosition();
            this.UpdateSelectionRectanglePosition();
        }

        public virtual void Highlight(bool on)
        {
            if (this.Initiated)
            {
                if(on)
                {
                    this.Label.FontWeight = FontWeights.Bold;
                    Panel parentPanel = (Panel)this.Parent;
                    int topMost = 0, zOrder;
                    for (int i = 0; i < parentPanel.Children.Count; i++)
                    {
                        if (parentPanel.Children[i] is Node)
                        {
                            Node node = (Node)parentPanel.Children[i];
                            zOrder = (int)node.GetValue(Canvas.ZIndexProperty);
                            if (zOrder > topMost)
                                topMost = zOrder;
                        }
                    }
                    SetValue(Canvas.ZIndexProperty, topMost + 1);
                    if(this.InputPort !=null)
                        this.InputPort.SetValue(Canvas.ZIndexProperty, topMost +2);
                    if (this.SuccessOutputPort != null)
                        this.SuccessOutputPort.SetValue(Canvas.ZIndexProperty, topMost + 2);
                    if (this.FailOutputPort != null)
                        this.FailOutputPort.SetValue(Canvas.ZIndexProperty, topMost + 2);
                }
                else
                    this.Label.FontWeight = FontWeights.Normal;
                this.Highlighted = (highlightColor == backgroundColor) ? false : true;
            }
        }

        public bool Disconnect(Connector c)
        {
            Panel parentPanel = (Panel) this.Parent;
            if(this.FromConnectors.Contains(c))
            {
                this.FromConnectors.Remove(c);
                Node fromNode = c.FromPort.OwnerNode;
                fromNode.ToConnectors.Remove(c);
            }
            else if(this.ToConnectors.Contains(c))
            {
                this.ToConnectors.Remove(c);
                Node toNode = c.ToPort.OwnerNode;
                toNode.FromConnectors.Remove(c);
            }
            parentPanel.Children.Remove(c);
            return true;
        }

        public bool Disconnect(Node node)
        {
            foreach(Connector fromConn in this.FromConnectors)
            {
                if(fromConn.FromPort.OwnerNode==node)
                {
                    return this.Disconnect(fromConn);
                }
            }
            foreach(Connector toConn in this.ToConnectors)
            {
                if (toConn.ToPort.OwnerNode == node)
                    return this.Disconnect(toConn);
            }
            return false;
        }

        public void Delete()
        {
            while(this.FromConnectors !=null && this.FromConnectors.Count>0)
            {
                Connector fromConn = this.FromConnectors[0];
                this.Disconnect(fromConn);
            }
            while (this.ToConnectors != null && this.ToConnectors.Count > 0)
            {
                Connector toConn = this.ToConnectors[0];
                this.Disconnect(toConn);
            }
            
            // path.Visibility = Visibility.Collapsed;
            Panel parent = (Panel) this.Parent;
            parent.Children.Remove(this);
            this.Selected = false;
        }

        public Connector MakeConnection(PortType outputPortType, Node toNode)
        {
            Port fromPort;
            if(outputPortType==PortType.ErrorOutput)
                fromPort = this.FailOutputPort;
            else
                fromPort = this.SuccessOutputPort;

            foreach(Connector toConn in this.ToConnectors)
            {
                if(toConn.FromPort==fromPort && toConn.ToPort==toNode.InputPort)
                    return toConn;
            }
            Panel parentPanel = (Panel) this.Parent;
            if (fromPort != null && toNode != null && toNode.InputPort != null)
            {
                Connector newConn = new Connector(fromPort, toNode.InputPort);
                parentPanel.Children.Add(newConn);
                int zIdx = Convert.ToInt32(this.GetValue(Canvas.ZIndexProperty));
                newConn.SetValue(Canvas.ZIndexProperty, Math.Max(1, zIdx - 1));
                this.ToConnectors.Add(newConn);
                toNode.FromConnectors.Add(newConn);
                return newConn;
            }
            else
                return null;
        }
        #endregion

        #region private methods
        void DrawOutput()
        {
            foreach(Connector toConn in this.ToConnectors)
            {
                toConn.RedrawConnection();
            }
        }
        #endregion
    }
}
